package com.xunan.demo.utils;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;

public class SignUtils {

    /**
     * 使用SHA256withRSA进行签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥数据
     * @return 签名数据
     */
    public static byte[] signSha256WithRsa(String data, PrivateKey privateKey) {
        //获取私钥信息
        String privateKeyStr = Base64.encodeBase64String(privateKey.getEncoded());

        //组装签名
        Sign signTool = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyStr, null);

        //签名
        return signTool.sign(data);
    }

    /**
     * 判断签名数据
     *
     * @param data 原始数据
     * @param sign 签名数据
     * @param publicKey 公钥
     * @return 是否相符
     */
    public static boolean verifySha256WithRsa(String data, byte[] sign, PublicKey publicKey) {
        //获取公钥信息
        String publicKeyStr = Base64.encodeBase64String(publicKey.getEncoded());

        //组装签名
        Sign signTool = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, publicKeyStr);

        //返回认证结果
        return signTool.verify(data.getBytes(StandardCharsets.UTF_8), sign);
    }


    /**
     * 解密请求回调
     * 参考https://developers.weixin.qq.com/community/develop/article/doc/000eeac2ba4898a9fd6be8b175bc13
     *
     * @param apiV3Key       微信API V3 秘钥
     * @param associatedData 附加数据
     * @param nonce          随机串
     * @param ciphertext     数据密文
     * @return 解密后的数据密文
     */
    public static String decryptWechatPaymentResponseBody(String apiV3Key, String associatedData, String nonce, String ciphertext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

            byte[] bytes;
            try {
                bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
